﻿using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using UIOC.CodeAnalysis.Diagnostics;
using UIOC.CodeAnalysis.Extensions;
using UIOC.CodeAnalysis.Helpers;

namespace UIOC.CodeAnalysis.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class InterfaceNamingAnalyzer : DiagnosticAnalyzer {

    private static void LogInfo(string message) {
        SourceOutputHelper.LogInfo(DiagnosticSeverity.Info, $"[{nameof(InterfaceNamingAnalyzer)}] {message}");
    }

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        => ImmutableArray.Create(InterfaceNamingDiagnostic.Rule);

    public override void Initialize(AnalysisContext context) {
        context.EnableConcurrentExecution();
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.RegisterSyntaxNodeAction(AnalyzeInterfaceDeclaration, SyntaxKind.InterfaceDeclaration);
    }

    private void AnalyzeInterfaceDeclaration(SyntaxNodeAnalysisContext context) {
        var compilation = context.Compilation;
        var assembly = compilation.Assembly;
        if (assembly.IsIgnore()) {
            return;
        }

        if (context.Node is not InterfaceDeclarationSyntax interfaceDeclarationSyntax) {
            return;
        }

        var interfaceSymbol = context.SemanticModel.GetDeclaredSymbol(interfaceDeclarationSyntax);
        if (interfaceSymbol == null)
            return;

        var interfaceName = interfaceDeclarationSyntax.Identifier.ValueText;

        if (!interfaceName.StartsWith("I") || interfaceName.Length < 2 || !char.IsUpper(interfaceName[1])) {
            var diagnostic = Diagnostic.Create(
                InterfaceNamingDiagnostic.Rule,
                interfaceDeclarationSyntax.Identifier.GetLocation(),
                interfaceName);

            context.ReportDiagnostic(diagnostic);
        }
    }
}

[Shared]
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(InterfaceNamingCodeFixProvider))]
public class InterfaceNamingCodeFixProvider : CodeFixProvider {

    private static void LogInfo(string message) {
        SourceOutputHelper.LogInfo(DiagnosticSeverity.Info, $"[{nameof(InterfaceNamingCodeFixProvider)}] {message}");
    }

    public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(InterfaceNamingDiagnostic.DiagnosticId);

    public sealed override FixAllProvider GetFixAllProvider() =>
        WellKnownFixAllProviders.BatchFixer;

    public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) {
        var document = context.Document;
        var syntaxTree = await document.GetSyntaxTreeAsync(context.CancellationToken).ConfigureAwait(false);
        if (syntaxTree == null) {
            return;
        }

        var root = await syntaxTree.GetRootAsync(context.CancellationToken).ConfigureAwait(false);

        // 定位接口声明节点
        var diagnostic = context.Diagnostics[0];
        var interfaceDeclaration = root.FindToken(diagnostic.Location.SourceSpan.Start)
            .Parent?.AncestorsAndSelf()
            .OfType<InterfaceDeclarationSyntax>()
            .FirstOrDefault();

        if (interfaceDeclaration == null) {
            return;
        }

        // 注册代码修复操作
        context.RegisterCodeFix(
            CodeAction.Create(
                title: "添加 'I' 前缀",
                createChangedDocument: ct => AddIPrefixAsync(document, interfaceDeclaration, ct),
                equivalenceKey: "添加 'I' 前缀"),
            diagnostic);
    }

    private async Task<Document> AddIPrefixAsync(Document document, InterfaceDeclarationSyntax interfaceDeclaration, CancellationToken cancellationToken) {
        // 创建新的接口名称
        var newInterfaceName = "I" + interfaceDeclaration.Identifier.ValueText;
        var newInterfaceIdentifier = SyntaxFactory.Identifier(newInterfaceName);

        // 创建修改后的接口声明
        var newInterfaceDeclaration = interfaceDeclaration.WithIdentifier(newInterfaceIdentifier);

        // 更新语法树
        var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
        var newRoot = oldRoot?.ReplaceNode(interfaceDeclaration, newInterfaceDeclaration);

        if (newRoot == null) {
            return document;
        }

        // 返回修改后的文档
        return document.WithSyntaxRoot(newRoot);
    }
}